/**
 * 如果设置一个索引大于length的值, 会隐式修改length
 */
let activeEffect
const bucket = new WeakMap()
let effectStack = []
const ITERATE_KEY = Symbol()
function track(target, key) {
  let depsMap = bucket.get(target)
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()))
  }
  let deps = depsMap.get(key)
  if (!deps) {
    depsMap.set(key, (deps = new Set()))
  }
  if (activeEffect) {
    deps.add(activeEffect)
    activeEffect.deps.push(deps)
  }
}

function trigger(target, key, type, value) {
  const depsMap = bucket.get(target)
  if (!depsMap) return
  const effects = depsMap.get(key)
  const effectsToRun = new Set()
  effects &&
    effects.forEach((effect) => {
      if (effect !== activeEffect) {
        effectsToRun.add(effect)
      }
    })

  if (type === 'ADD' || type === 'DELETE') {
    const iterateEffects = depsMap.get(ITERATE_KEY)

    iterateEffects &&
      iterateEffects.forEach((effect) => {
        if (effect !== activeEffect) {
          effectsToRun.add(effect)
        }
      })
    // 如果是数组执行新增逻辑的时候
    if (type === 'ADD' && Array.isArray(target)) {
      const lengthEffects = depsMap.get('length')
      lengthEffects &&
        lengthEffects.forEach((effectFn) => {
          if (effectFn !== activeEffect) {
            effectsToRun.add(effectFn)
          }
        })
    }
  }

  // 如果是修改length
  if (key === 'length') {
    // 将本数组的所有依赖,都拿出来遍历, 键大于或等于length的, 都要更新
    depsMap.forEach((deps, key) => {
      // 如果是超出length长度的键的依赖
      if (key >= value) {
        // 遍历依赖
        deps.forEach((effectFn) => {
          if (effectFn !== activeEffect) {
            effectsToRun.add(effectFn)
          }
        })
      }
    })
  }

  effectsToRun.forEach((effect) => {
    if (effect.options && effect.options.schedule) {
      effect.options.schedule(effect)
    } else {
      effect()
    }
  })
}
function createReactive(data, isShallow = false, isReadonly = false) {
  const obj = new Proxy(data, {
    get(target, key, receiver) {
      if (key === 'raw') {
        return target
      }
      const res = Reflect.get(target, key, receiver)
      if (!isReadonly) {
        track(target, key)
      }

      if (isShallow) {
        return res
      }

      if (typeof res === 'object' && res !== null) {
        return isReadonly ? createReactive(res, false, true) : createReactive(res)
      }

      return res
    },
    set(target, key, value, receiver) {
      if (isReadonly) {
        console.log(`属性${key}为只读`)
        return true
      }
      const oldValue = target[key]
      // 加入数组判断, 如果是数组, 而且key小于或等于length, 则是set, 否则是add
      const type = Array.isArray(target)
        ? Number(key) < target.length
          ? 'SET'
          : 'ADD'
        : Object.prototype.hasOwnProperty.call(target, key)
        ? 'SET'
        : 'ADD'
      const res = Reflect.set(target, key, value, receiver)
      if (receiver.raw === target) {
        if (oldValue !== value && (oldValue === oldValue || value === value)) {
          // 将值传给trigger
          trigger(target, key, type, value)
        }
      }
      return res
    },
    has(target, key) {
      return Reflect.has(target, key)
    },
    ownKeys(target) {
      track(target, ITERATE_KEY)
      return Reflect.ownKeys(target)
    },
    deleteProperty(target, key) {
      if (isReadonly) {
        console.log(`${key}是只读数据不可删除`)
        return true
      }
      const hadKey = Object.prototype.toString.call(target, key)
      const res = Reflect.deleteProperty(target, key)
      if (hadKey && res) {
        trigger(target, key, 'DELETE')
      }
      return res
    }
  })
  return obj
}
function cleanup(effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i]
    deps.delete(effectFn)
  }
  effectFn.deps.length = 0
}

function effect(fn, options = {}) {
  const effectFn = () => {
    cleanup(effectFn)
    activeEffect = effectFn
    effectStack.push(effectFn)
    const res = fn()
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
    return res
  }
  effectFn.deps = []
  effectFn.options = options
  if (!options.lazy) {
    effectFn()
  }
  return effectFn
}

function readonly(obj) {
  return createReactive(obj, false, true)
}

function shallowReadonly(obj) {
  return createReactive(obj, true, true)
}

// 测试
const arr = createReactive([1, 2, 3])

// 修改元素, length响应
// effect(() => {
//   console.log(arr.length, 'length')
// })

// arr[2] = '111'

// 修改length, 元素响应

effect(() => {
  console.log(arr[2], 'arr')
})

arr.length = 0
